package 基础语法;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@AnnoName2("可以不写名字value，注解在类上")
@AnnoName1(annoMethod2 = "aa", value = "aass") // 必须写名字
@AnnoName3(annoMethod2 = "") // 必须写名字annoMethod2
// 有default的名字可以不写
@AnnoName4(annoBool = false, annoChar = 0, annoClass = String.class, annoEnum1 = MyEnum.B, annoString = "", anotherAnno1 = @AnnoName3(annoMethod2 = ""), annoArray = {
        "ss", "asa" })
public class 注解 {
    @AnnoName2("可以不写名字value,注解在类的属性上")
     int a;

    @AnnoName2("可以不写名字value,注解在构造器上")
    注解() {
    }

    @AnnoName2("可以不写名字value,注解在方法上")
    public static void main(@AnnoName2("可以不写名字value，注解在方法参数中") String[] args)
            throws NoSuchMethodException, SecurityException, NoSuchFieldException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {// AnnoName2不加上@Retention(RetentionPolicy.RUNTIME)可能报错
        @AnnoName2("可以不写名字value,注解在方法内的变量上") // 这个注解反射是获取不到的
        int a;
        解析注解();
    }

    static void 解析注解() throws NoSuchMethodException, SecurityException, NoSuchFieldException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // 解析什么地方的注解，就先获取这个地方，比如Class，Contuctor,Filed,Method

        Class myClass = 注解.class;

        // 判断类上有没有某个注解
        if (myClass.isAnnotationPresent(AnnoName2.class)) {// 注意AnnoName2要加上@Retention(RetentionPolicy.RUNTIME) ，不然获取不到
            AnnoName2 annoName2 = (AnnoName2) myClass.getAnnotation(AnnoName2.class);
            System.out.println(annoName2.value());
        }
        // 判断方法上有没有注解
        Method m = myClass.getDeclaredMethod("main", String[].class);
        if (m.isAnnotationPresent(AnnoName2.class)) {
            AnnoName2 annoName2 = m.getAnnotation(AnnoName2.class);
            System.out.println(annoName2.value());
             // m.invoke(new 注解(),null);//有问题
            //m.invoke(new 注解(), (Object) new String[]{"hello"});//没问题
           // m.invoke(new 注解(), (Object) null);//没问题。注意这里要强制转换 (Object)，否则会被当作可变参数展开。
           
        }
        // 获取方法参数上的注解
        Parameter[] ps = m.getParameters();
        if ((ps[0].isAnnotationPresent(AnnoName2.class))) {
            AnnoName2 annoName2 = ps[0].getAnnotation(AnnoName2.class);
            System.out.println(annoName2.value());
        }
        //获取属性上的注解
        Field f = myClass.getDeclaredField("a");//这个属性没有用public修饰，用getField获取 不到
       f.setAccessible(true);
        if ((f.isAnnotationPresent(AnnoName2.class))) {
            AnnoName2 annoName2 = f.getAnnotation(AnnoName2.class);
            System.out.println(annoName2.value());
        }

        //获取构造器上的注解
        Constructor c=myClass.getDeclaredConstructor();//获取无参构造器.这个构造器也没有用public修饰
        c.setAccessible(true);
        if((c.isAnnotationPresent(AnnoName2.class))){
             AnnoName2 annoName2 = (AnnoName2) c.getAnnotation(AnnoName2.class);
            System.out.println(annoName2.value());
        }


    }
}

/*
 * 【E:\桌面\JAVA面试\Java语法与工具学习\基础语法>】
 * 编译：
 * javac -encoding UTF-8 注解.java
 * 注意反编译的是AnnoName
 * 反编译：
 * javap AnnoName1.class
 * interface 基础语法.AnnoName1 extends java.lang.annotation.Annotation {
 * public abstract java.lang.String annoMethod2();
 * public abstract java.lang.String value();
 * }
 * 可见注解本质是一个接口，继承自Annotation
 */
@interface AnnoName1 {
    String annoMethod2();

    String value();
}

@Retention(RetentionPolicy.RUNTIME) // 元注解
@interface AnnoName2 {
    String value();// 特殊属性，在使用时如果只有一个value属性，value名称可不写
}

@interface AnnoName3 {
    String annoMethod2();
}

@interface AnnoName4 {// 注解的返回值只有基本类型，String，class及其子类，枚举，注解，以及他们的数组
    String annoString();

    String annoString1() default "090as";

    int annoInt() default 10;

    AnnoName3 anotherAnno() default @AnnoName3(annoMethod2 = "as");

    AnnoName3 anotherAnno1();

    Class annoClass();

    boolean annoBool();

    char annoChar();

    MyEnum annoEnum() default MyEnum.A;

    MyEnum annoEnum1();

    String[] annoArray();

    @AnnoName1(annoMethod2 = "", value = "") // 如果不用@Target限制，注解可以到处用
    String[] annoArray1() default { "ss", "asa" };

}

// 生命注解的保留周期
// source:只作用在源码阶段，字节码文件中不存在
// class(默认值)：保留到字节码文件阶段，.class文件中有。运行阶段不存在
// runtime：一直保留到运行阶段
@Retention(RetentionPolicy.RUNTIME) // 元注解
// 生命被修饰的注解只能 在哪些位置使用。去看ElementType的源码
@Target({ ElementType.METHOD }) // 元注解。这里表示只能注解方法。源码： ElementType[] value();只有一个元素时，{}可省略
@interface TestMeta {

}

enum MyEnum {
    A, B, C, D;
}
